package ciicos.util;

import ciicos.base.Pagination;
import ciicos.entity.*;
import ciicos.model.visiual.superQuery.ConditionJsonModel;
import ciicos.model.visiual.superQuery.SuperQueryConditionModel;
import ciicos.model.visiual.superQuery.SuperQueryJsonModel;
import ciicos.util.relation.SubRealtion;
import ciicos.util.relation.WrapperRelation;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.IService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.stream.Collectors;

@Component
public class AutoCodeUtil {

    @Autowired
    private GeneraterSwapUtil generaterSwapUtil;

    public static <T, R, W> Map<R, W> getMapByCodeList(IService<T> service, List<R> codeList,
                                                           SFunction<T, ? extends R> codeGetter,
                                                           Function<T, ? extends W> valueGetter) {

        List<T> entityList = getListByCodeList(service, codeList, codeGetter);
        Map<R, W> map = entityList.stream().collect(Collectors.toMap(codeGetter, valueGetter));
        return map;
    }

    public static <T, R> Map<R, String> getMapByCodeList(IService<T> service, List<R> codeList,
                                                             SFunction<T, ? extends R> codeGetter,
                                                             Function<T, String> valueGetter,
                                                             boolean defaultcode) {
        Map<R, String> map = getMapByCodeList(service, codeList, codeGetter, valueGetter);
        if(defaultcode) {
            codeList.forEach(r -> map.computeIfAbsent(r, k -> r.toString()));
        }
        return map;
    }

    public static <T, R> Map<R, T> getMapByCodeList(IService<T> service, List<R> codeList,
                                                        SFunction<T, ? extends R> codeGetter) {

        List<T> entityList = getListByCodeList(service, codeList, codeGetter);
        Map<R, T> map = entityList.stream().collect(Collectors.toMap(codeGetter, Function.identity()));
        return map;
    }

    public static <T, R, W> Map<R, List<W>> getMapListByCodeList(IService<T> service, List<R> codeList,
                                                       SFunction<T, ? extends R> codeGetter,
                                                       Function<T, ? extends W> valueGetter) {

        List<T> entityList = getListByCodeList(service, codeList, codeGetter);
        Map<R, List<W>> map = entityList.stream().collect(Collectors.groupingBy(codeGetter, Collectors.mapping( valueGetter, Collectors.toList())));
        return map;
    }

    public static <T, R> Map<R, List<String>> getMapListByCodeList(IService<T> service, List<R> codeList,
                                                         SFunction<T, ? extends R> codeGetter,
                                                         Function<T, String> valueGetter,
                                                         boolean defaultcode) {
        Map<R, List<String>> map = getMapListByCodeList(service, codeList, codeGetter, valueGetter);
        if(defaultcode) {
            codeList.forEach(r -> map.computeIfAbsent(r, k -> new ArrayList<>()));
        }
        return map;
    }

    public static <T, R> Map<R, List<T>> getMapListByCodeList(IService<T> service, List<R> codeList,
                                                              SFunction<T, ? extends R> codeGetter) {

        List<T> entityList = getListByCodeList(service, codeList, codeGetter);
        Map<R, List<T>> map = entityList.stream().collect(Collectors.groupingBy(codeGetter));
        return map;
    }

    public static <T, R> List<T> getListByCodeList(IService<T> service, List<R> codeList,
                                                   SFunction<T, ? extends R> codeGetter) {
        QueryWrapper<T> queryWrapper = new QueryWrapper<>();
        if(ListUtil.isEmpty(codeList)){
            return new ArrayList<>();
        }
        queryWrapper.lambda().in(codeGetter, codeList);
        List<T> entityList = service.list(queryWrapper);
        if(entityList == null){
            entityList = new ArrayList<>();
        }
        return entityList;
    }

    public static <T, R> List<T> getListByCode(IService<T> service, R code,
                                               SFunction<T, ? extends R> codeGetter) {
        QueryWrapper<T> queryWrapper = new QueryWrapper<>();
        if(ObjectUtil.isEmpty(code)){
            return null;
        }
        queryWrapper.lambda().eq(codeGetter, code);
        List<T> entityList = service.list(queryWrapper);
        if(entityList == null){
            entityList = new ArrayList<>();
        }
        return entityList;
    }

    public static <T, R, W> List<W> getListByCode(IService<T> service, R code,
                                                  SFunction<T, ? extends R> codeGetter,
                                                  SFunction<T, ? extends W> valueGetter) {

        List<T> entityList = getListByCode(service,code,codeGetter);
        List<W> resultList = entityList.stream().map(valueGetter).collect(Collectors.toList());
        return resultList;
    }

    public static <T, R> T getOneByCode(IService<T> service, R code,
                                        SFunction<T, ? extends R> codeGetter) {
        QueryWrapper<T> queryWrapper = new QueryWrapper<>();
        if(ObjectUtil.isEmpty(code)){
            return null;
        }
        queryWrapper.lambda().eq(codeGetter, code);
        T entity = service.getOne(queryWrapper);
        return entity;
    }

    public static <T, R, W> String getValueByOneCode(IService<T> service, R code,
                                        SFunction<T, ? extends R> codeGetter,
                                          Function<T, ?extends W> valueGetter,boolean defaultcode) {
        QueryWrapper<T> queryWrapper = new QueryWrapper<>();
        if(ObjectUtil.isEmpty(code)){
            return "";
        }
        queryWrapper.lambda().eq(codeGetter, code);
        T entity = service.getOne(queryWrapper);
        W v = null;
        if(entity !=null){
            v = valueGetter.apply(entity);
        }
        if(v==null){
            if(defaultcode){
                return code.toString();
            }else{
                return "";
            }
        }else{
            return v.toString();
        }
    }

    public static <T, R> List<R> distinct(IService<T> service, SFunction<T, R> codeGetter) {
        QueryWrapper<T> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda().select(codeGetter).groupBy(codeGetter);
        List<R> rList = (List<R>) service.getBaseMapper().selectObjs(queryWrapper);
        return rList;
    }

    public static <T, R> void sort(QueryWrapper<T> queryWrapper, Class<T> eClass, String sidx, String sort, SFunction<T, R> defaultSidxGetter,boolean defaultdesc) {
        if (StringUtil.isEmpty(sidx)) {
            if(defaultdesc) {
                queryWrapper.lambda().orderByDesc(defaultSidxGetter);
            }else{
                queryWrapper.lambda().orderByAsc(defaultSidxGetter);
            }
        } else {
            try {
                Field declaredField = eClass.getDeclaredField(sidx);
                declaredField.setAccessible(true);
                String value = declaredField.getAnnotation(TableField.class).value();
                if (StringUtil.equals("asc", sort.toLowerCase())) {
                    queryWrapper.orderByAsc(value);
                } else {
                    queryWrapper.orderByDesc(value);
                }
            } catch (NoSuchFieldException e) {
                e.printStackTrace();
            }
        }
    }

    public static <T> List<T> pageList(IService<T> service,QueryWrapper<T> queryWrapper,Pagination pagination,boolean isPage) {
        if(isPage){
            Page<T> page = new Page<>(pagination.getCurrentPage(), pagination.getPageSize());
            IPage<T> userIPage = service.page(page, queryWrapper);
            return pagination.setData(userIPage.getRecords(), userIPage.getTotal());
        }else{
            return service.list(queryWrapper);
        }
    }

    public static <T, R> List<T> sortPageList(IService<T> service,QueryWrapper<T> queryWrapper, Class<T> eClass, String sidx, String sort, SFunction<T, R> defaultSidxGetter,boolean defaultdesc,Pagination pagination,boolean isPage) {
        sort(queryWrapper,eClass,sidx,sort,defaultSidxGetter,defaultdesc);
        return pageList(service,queryWrapper,pagination,isPage);
    }

    public static SuperQueryJsonModel parsequeryJson(String superQueryJson){
        if (ObjectUtil.isEmpty(superQueryJson)){
            return null;
        }
        SuperQueryJsonModel superQueryJsonModel = JsonUtil.getJsonToBean(superQueryJson, SuperQueryJsonModel.class);
        return superQueryJsonModel;
    }
    public static <T> int highQuery(String superQueryJson,Class<T> eclass,QueryWrapper<T> queryWrapper) {
        return highQuery(
                parsequeryJson(superQueryJson),
                eclass,queryWrapper
        );
    }

    public static <T> int highQuery(SuperQueryJsonModel superQueryJsonModel,Class<T> eclass,QueryWrapper<T> queryWrapper) {
        return highQuery(superQueryJsonModel,queryWrapper,new WrapperRelation<>(eclass));
    }

    public static <T> int highQuery(String superQueryJson,QueryWrapper<T> queryWrapper,WrapperRelation<T> wrapperRelation) {
        return highQuery(
                parsequeryJson(superQueryJson),
                queryWrapper,wrapperRelation
        );
    }

    public static <T> int highQuery(SuperQueryJsonModel superQueryJsonModel,QueryWrapper<T> queryWrapper,WrapperRelation<T> wrapperRelation) {
        if (superQueryJsonModel == null){
            return 0;
        }else{
            String matchLogic = superQueryJsonModel.getMatchLogic();
            List<ConditionJsonModel> superQueryList = JsonUtil.getJsonToList(superQueryJsonModel.getConditionJson(), ConditionJsonModel.class);
            for (ConditionJsonModel conditionjson : superQueryList){
                Map<String, Object> map = JsonUtil.stringToMap(conditionjson.getAttr());
                Map<String, Object> configMap = JsonUtil.stringToMap(map.get("__config__").toString());
                String tableName = configMap.get("relationTable")!=null ? String.valueOf(configMap.get("relationTable")) : String.valueOf(configMap.get("tableName"));
                if (map.get("multiple") != null) {
                    if (Boolean.valueOf(String.valueOf(map.get("multiple"))) && ObjectUtil.isNull(conditionjson.getFieldValue())) {
                        conditionjson.setFieldValue("[]");
                    }
                }
                conditionjson.setTableName(tableName);
            }
            Class meClass = wrapperRelation.getMeClass();
            String tableName = ((TableName)meClass.getAnnotation(TableName.class)).value();
            List<Integer> numList = new ArrayList<>();
            queryWrapper.and(t->
            {
                numList.add(getCondition(
                        new SuperQueryConditionModel(t,superQueryList,matchLogic,tableName)
                        ,meClass));
                for (SubRealtion subRealtion : wrapperRelation.getSubRealtionList()) {
                    Class seClass = subRealtion.getSeClass();
                    String seTableName = ((TableName)seClass.getAnnotation(TableName.class)).value();
                    String seColumn = subRealtion.getSubColumn();

                    QueryWrapper subQueryWrapper = new QueryWrapper();
                    int num = getCondition(
                            new SuperQueryConditionModel(subQueryWrapper,superQueryList,matchLogic,seTableName)
                            ,subRealtion.getSeClass());
                    if(num == 0){
                        continue;
                    }
                    numList.add(num);
                    subQueryWrapper.select(getTableFieldName(seClass,seColumn));
                    List subIdlist = subRealtion.getService().getBaseMapper().selectObjs(subQueryWrapper);

                    if ("AND".equalsIgnoreCase(matchLogic)) {
                        t.in(getTableFieldName(meClass,subRealtion.getMainColumn()),subIdlist);
                    }else{
                        t.or(to->to.in(getTableFieldName(meClass,subRealtion.getMainColumn()),subIdlist));
                    }
                }

            });
            return numList.get(0);
        }
    }

    public static String getTableFieldName (Class eclass,String fieldname){
        Field declaredField =null;
        try {
            declaredField = eclass.getDeclaredField(fieldname);
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        }

        declaredField.setAccessible(true);
        TableField tableField = declaredField.getAnnotation(TableField.class);
        if(tableField!=null){
            return tableField.value();
        }
        TableId tableId = declaredField.getAnnotation(TableId.class);
        if(tableId!=null){
            return tableId.value();
        }
        return null;

    }


    private static <E> Integer getCondition(SuperQueryConditionModel<E> conditionModel, Class<E> eclass) {
        int num = 0;
        QueryWrapper<E> tw = conditionModel.getObj();
        List<ConditionJsonModel> queryConditionModels = conditionModel.getConditionList();
        String op = conditionModel.getMatchLogic();
        String tableName = conditionModel.getTableName();
        List<ConditionJsonModel> useCondition = new ArrayList();

        for(ConditionJsonModel queryConditionModel : queryConditionModels) {
            if (queryConditionModel.getTableName().equalsIgnoreCase(tableName)) {
                if (queryConditionModel.getField().contains("jnpf")) {
                    String child = queryConditionModel.getField();
                    String s1 = child.substring(child.lastIndexOf("jnpf_")).replace("jnpf_", "");
                    queryConditionModel.setField(s1);
                }

                if (queryConditionModel.getField().startsWith("tableField")) {
                    String child = queryConditionModel.getField();
                    String s1 = child.substring(child.indexOf("-") + 1);
                    queryConditionModel.setField(s1);
                }

                useCondition.add(queryConditionModel);
            }
        }

        if (queryConditionModels.size() >= 1 && useCondition.size() >= 1) {
            if (useCondition.size() > 0) {
                ++num;
            }

            for(ConditionJsonModel queryConditionModel : useCondition) {
                String jnpfKey = queryConditionModel.getJnpfKey();
                String fieldValue = queryConditionModel.getFieldValue();
                if (StringUtil.isEmpty(fieldValue)) {
                    if (!jnpfKey.equals("cascader") && !jnpfKey.equals("checkbox") && !jnpfKey.equals("comSelect") && !jnpfKey.equals("address")) {
                        queryConditionModel.setFieldValue("");
                    } else {
                        queryConditionModel.setFieldValue("[]");
                    }

                    if (queryConditionModel.getSymbol().equals("like")) {
                        queryConditionModel.setSymbol("==");
                    } else if (queryConditionModel.getSymbol().equals("notLike")) {
                        queryConditionModel.setSymbol("<>");
                    }
                }

                if (jnpfKey.equals("date")) {
                    String startTime = "";
                    if (StringUtil.isNotEmpty(fieldValue)) {
                        Long o1 = Long.valueOf(fieldValue);
                        startTime = DateUtil.daFormat(o1);
                    }

                    queryConditionModel.setFieldValue(startTime);
                } else if (!jnpfKey.equals("createTime") && !jnpfKey.equals("modifyTime")) {
                    if (jnpfKey.equals("currOrganize") && StringUtil.isNotEmpty(fieldValue)) {
                        List<String> orgList = JsonUtil.getJsonToList(fieldValue, String.class);
                        queryConditionModel.setFieldValue((String)orgList.get(orgList.size() - 1));
                    }
                } else {
                    String startTime = "";
                    if (StringUtil.isNotEmpty(fieldValue)) {
                        Long o1 = Long.valueOf(fieldValue);
                        startTime = DateUtil.daFormatHHMMSS(o1);
                    }

                    queryConditionModel.setFieldValue(startTime);
                }
            }


            for(ConditionJsonModel conditionJsonModel : useCondition) {
                    String conditionField = conditionJsonModel.getField();
                    String jnpfKey = conditionJsonModel.getJnpfKey();
                    Field declaredField = null;

                    try {
                        declaredField = eclass.getDeclaredField(conditionField);
                    } catch (NoSuchFieldException e) {
                        e.printStackTrace();
                    }

                    declaredField.setAccessible(true);
                    String field = ((TableField)declaredField.getAnnotation(TableField.class)).value();
                    String fieldValue = conditionJsonModel.getFieldValue();
                    String symbol = conditionJsonModel.getSymbol();
                    if ("AND".equalsIgnoreCase(op)) {
                        if (symbol.equals("==")) {
                            tw.and((qw) -> {
                                if (!jnpfKey.equals("numInput") && !jnpfKey.equals("calculate")) {
                                    qw.eq(field, fieldValue);
                                } else if (StringUtil.isNotEmpty(fieldValue)) {
                                    qw.eq(field, fieldValue);
                                }

                                if (StringUtil.isEmpty(fieldValue)) {
                                    qw.or((ew) -> ew.isNull(field));
                                }

                            });
                        } else if (symbol.equals(">=")) {
                            tw.ge(field, fieldValue);
                        } else if (symbol.equals("<=")) {
                            tw.and((ew) -> {
                                ew.le(field, fieldValue);
                                ew.and((qw) -> qw.ne(field, ""));
                            });
                        } else if (symbol.equals(">")) {
                            tw.gt(field, fieldValue);
                        } else if (symbol.equals("<")) {
                            tw.and((ew) -> {
                                ew.lt(field, fieldValue);
                                ew.and((qw) -> qw.ne(field, ""));
                            });
                        } else if (symbol.equals("<>")) {
                            tw.and((ew) -> {
                                ew.ne(field, fieldValue);
                                if (StringUtil.isNotEmpty(fieldValue)) {
                                    ew.or((qw) -> qw.isNull(field));
                                } else {
                                    ew.and((qw) -> qw.isNotNull(field));
                                }

                            });
                        } else if (symbol.equals("like")) {
                            tw.and((ew) -> {
                                if (StringUtil.isNotEmpty(fieldValue)) {
                                    ew.like(field, fieldValue);
                                } else {
                                    ew.isNull(field);
                                }

                            });
                        } else if (symbol.equals("notLike")) {
                            tw.and((ew) -> {
                                if (StringUtil.isNotEmpty(fieldValue)) {
                                    ew.notLike(field, fieldValue);
                                    ew.or((qw) -> qw.isNull(field));
                                } else {
                                    ew.isNotNull(field);
                                }

                            });
                        }
                    } else if (symbol.equals("==")) {
                        tw.or((qw) -> qw.eq(field, fieldValue));
                    } else if (symbol.equals(">=")) {
                        tw.or((qw) -> qw.ge(field, fieldValue));
                    } else if (symbol.equals("<=")) {
                        tw.or((qw) -> qw.le(field, fieldValue));
                    } else if (symbol.equals(">")) {
                        tw.or((qw) -> qw.gt(field, fieldValue));
                    } else if (symbol.equals("<")) {
                        tw.or((qw) -> qw.lt(field, fieldValue));
                    } else if (symbol.equals("<>")) {
                        tw.or((qw) -> qw.ne(field, fieldValue));
                        if (StringUtil.isNotEmpty(fieldValue)) {
                            tw.or((qw) -> qw.isNull(field));
                        }
                    } else if (symbol.equals("like")) {
                        if (StringUtil.isNotEmpty(fieldValue)) {
                            tw.or((qw) -> qw.like(field, fieldValue));
                        } else {
                            tw.or((qw) -> qw.isNull(field));
                        }
                    } else if (symbol.equals("notLike")) {
                        if (StringUtil.isNotEmpty(fieldValue)) {
                            tw.or((qw) -> qw.notLike(field, fieldValue));
                            tw.or((qw) -> qw.isNull(field));
                        } else {
                            tw.or((qw) -> qw.isNotNull(field));
                        }
                    }
                }


            return num;
        } else {
            return num;
        }
    }


}
